﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Antlr.Runtime;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SimpleSpreadsheet.Core;
using SimpleSpreadsheet.Core.Parser;
using SimpleSpreadsheet.Expressions;
using SimpleSpreadsheet.Functions;


namespace SimpleSpreadsheet.Test
{
    /// <summary>
    /// Summary description for ExpressionTest
    /// </summary>
    [TestClass]
    public class ExpressionTest
    {
        private Dictionary<string, IFunction> Functions { get; set; }

        public ExpressionTest()
        {
            Functions = new Dictionary<string, IFunction>
                            {
                                {"SUM", new Sum()},
                                {"MIN", new Min()},
                                {"MAX", new Max()},
                                {"AVERAGE", new Average()},
                                {"REDUCE", new Reduce()}
                            };
        }

        private class ReferenceValuesProvider : IValueProvider
        {
            #region Implementation of IValueProvider

            public decimal GetValueByName(string name)
            {
                switch (name)
                {
                    case "A1":
                        return 1;
                    case "A2":
                        return 2;
                    case "A3":
                        return 3;
                    case "B1":
                        return 0;
                    case "B2":
                        return 2;
                    case "C3":
                        return 3;
                    default:
                        return 0;
                }
            }

            #endregion
        }

        [TestMethod]
        public void ReferenceEvaluation()
        {
            string expressionText = "A1+B2*C3";

            ExpressionLexer lexer = new ExpressionLexer(new ANTLRStringStream(expressionText));
            ExpressionParser parser = new ExpressionParser(new CommonTokenStream(lexer));

            Expression expression = new Expression(expressionText, Functions, parser);
            expression.ReferenceValueProvider = new ReferenceValuesProvider();
            var result = Convert.ToDecimal(expression.Evaluate());
            
            Assert.AreEqual(result, 7m);
        }

        [TestMethod]
        public void SumEvaluation()
        {
            string expressionText = "SUM(A1:B2,C3)";

            ExpressionLexer lexer = new ExpressionLexer(new ANTLRStringStream(expressionText));
            ExpressionParser parser = new ExpressionParser(new CommonTokenStream(lexer));

            Expression expression = new Expression(expressionText, Functions, parser);
            expression.ReferenceValueProvider = new ReferenceValuesProvider();
            var result = Convert.ToDecimal(expression.Evaluate());

            Assert.AreEqual(result, 8m);
        }

        [TestMethod]
        public void MinEvaluation()
        {
            string expressionText = "MIN(A1,C3,A2)";

            ExpressionLexer lexer = new ExpressionLexer(new ANTLRStringStream(expressionText));
            ExpressionParser parser = new ExpressionParser(new CommonTokenStream(lexer));

            Expression expression = new Expression(expressionText, Functions, parser);
            expression.ReferenceValueProvider = new ReferenceValuesProvider();
            var result = Convert.ToDecimal(expression.Evaluate());

            Assert.AreEqual(result, 1m);
        }

        [TestMethod]
        public void MaxEvaluation()
        {
            string expressionText = "MAX(C3,2,-2,1/2)";

            ExpressionLexer lexer = new ExpressionLexer(new ANTLRStringStream(expressionText));
            ExpressionParser parser = new ExpressionParser(new CommonTokenStream(lexer));

            Expression expression = new Expression(expressionText, Functions, parser);
            expression.ReferenceValueProvider = new ReferenceValuesProvider();
            var result = Convert.ToDecimal(expression.Evaluate());

            Assert.AreEqual(result, 3m);
        }

        [TestMethod]
        public void AverageEvaluation()
        {
            string expressionText = "AVERAGE(A1:A2,B1:B2)";

            ExpressionLexer lexer = new ExpressionLexer(new ANTLRStringStream(expressionText));
            ExpressionParser parser = new ExpressionParser(new CommonTokenStream(lexer));

            Expression expression = new Expression(expressionText, Functions, parser);
            expression.ReferenceValueProvider = new ReferenceValuesProvider();
            var result = Convert.ToDecimal(expression.Evaluate());

            Assert.AreEqual(result, 1.25m);
        }

        [TestMethod]
        public void ReduceEvaluation()
        {
            string expressionText = "REDUCE(A1:A3,1,X*Y)";

            ExpressionLexer lexer = new ExpressionLexer(new ANTLRStringStream(expressionText));
            ExpressionParser parser = new ExpressionParser(new CommonTokenStream(lexer));

            Expression expression = new Expression(expressionText, Functions, parser);
            expression.ReferenceValueProvider = new ReferenceValuesProvider();
            var result = Convert.ToDecimal(expression.Evaluate());

            Assert.AreEqual(result, 6m);
        }

    }
}
